
open Ast
open Ast_fullfill
open Ast_absolute
open Ast_effectable
open Ast_memory

(* TODO
	Gerer les bool exact pour objets
	Gerer les contextes pour variables (window[])
	Gerer la memoire pour les ifs
*)

exception Logic_error

let rec inline ast =

	let rec inline_list func declIdents variables isCertain context = function
		| [] -> defaultReturn, [], declIdents, variables, []
		| a::b -> let value, sideEffects, declIdents, variables, element = func declIdents variables isCertain context a in
			if value.returnsAlways || value.throws || value.anonymousBreak || (value.breaks <> []) then
				value, sideEffects, declIdents, variables, element::b
			else
				let value1, sideEffects1, declIdents, variables, sublist = inline_list func declIdents variables isCertain context b in
				{
					returns = value.returns@value1.returns;
					returnsAlways = value1.returnsAlways;
					throws = value1.throws;
					breaks = value1.breaks;
					anonymousBreak = value1.anonymousBreak
				}, sideEffects@sideEffects1, declIdents, variables, element::sublist in

	let add_calls_to_stmt declIdents calls stmt =
		if calls = StrMap.empty then
		declIdents, stmt
		else
		let declIdents, labels, absolute = declIdents in
		let (declIdents,labels,vars,stmts) = StrMap.fold (fun ident (refPath,args,values,content) (declIdents,labels,vars,stmts) -> 
			(
				let values = if (List.length values) < (List.length args) then
					list_fill_right ((List.length args) - (List.length values)) 
						(ACondExpr(ACondBinaryExpr(AUnaryExpr(APostfixExpr(
						ALeftHandSide(ANewExpression(AMemberExpression(
						APrimaryExpr(AIdentifier("?")
						))))))))) values
					else (list_sub (List.length args) values) in
			
				let vars = (ident,None)::vars in
				let declIdents, identsMap, vars = List.fold_left2 (fun (declIdents,identsMap,vars) a v -> 
					let identsMap,declIdents,_ = Tools_identifier.free_ident (identsMap,declIdents, absolute) a false in
					let a = StrMap.find a identsMap in
					declIdents,identsMap,(a,match v with
						| ACondExpr(ACondBinaryExpr(AUnaryExpr(APostfixExpr(
						ALeftHandSide(ANewExpression(AMemberExpression(
						APrimaryExpr(AIdentifier("?")
						)))))))) -> None
						| _ -> Some v)::vars)
					(declIdents, StrMap.empty, vars) args values in
				let identsMap = StrMap.add "?" ident identsMap in
				let labelsMap,labels,_ = Tools_identifier.free_ident (StrMap.empty,labels, absolute) "-" false in
				let label = StrMap.find "-" labelsMap in
				let declIdents, labels, stmts2 = Inliner_transform.inline_program (refPath,identsMap,labelsMap,declIdents,labels,absolute) content in
				let stmts = ALabelledStatement (label, ABlock (stmts2)) :: stmts in
				declIdents, labels, vars, stmts
			)) calls (declIdents, labels, [],[stmt]) in
		(declIdents,labels,absolute),ABlock(if vars = [] then stmts else AVariableStatement(true,false,vars)::stmts)
		in

	let rec set_value variables isCertain context value expr = 
		memory_set_value inline_realFunctionContent variables isCertain context value expr

	and get_value variables isCertain context refValues =
		memory_get_value inline_realFunctionContent variables isCertain context refValues

	and erase_sideEffects variables isCertain context = 
		memory_erase_sideEffects inline_realFunctionContent variables isCertain context

	and inline_program program = 
	
		let variables = create_env () in
	
		let fullfill = fullfill_list fullfill_sourceElement {
			idents = StrSet.empty;
			labels = StrSet.empty;
			locals = StrSet.empty;
			} program in
		let absolute = absolute_list absolute_sourceElement {
			abs_labels = StrSet.empty;
			abs_idents = StrSet.empty
		} StrSet.empty program in
		let context = {
			co_context = 0;
			co_current = None;
			co_nesting = 0
			} in
		
		let functions = Ast_tools.funcDecls_to_map program in
		StrSet.fold (fun ident _ -> ignore(set_value variables true context VUndefined (VString ident)) ) fullfill.idents ();
		StrMap.fold (fun ident (is_inline,params,body) _ -> 
			let index = create_function is_inline params body true in
			ignore(set_value variables true context (VMemory index) (VString ident))) functions ();
		let _, _, _, _, ast = inline_list (inline_sourceElement StrSet.empty) (fullfill.idents,absolute.abs_labels,absolute.abs_idents) variables true context program in 
			ast

	and inline_sourceElement labels declIdents variables isCertain context = function
		| AStatement(stmt) -> let value, sideEffects, declIdents, variables, stmt = inline_statement labels declIdents variables isCertain context stmt in 
			value, sideEffects, declIdents, variables, AStatement(stmt)
		(*| AFunctionDeclaration(funcDecl) -> let value, sideEffects, variables, funcDecl = inline_functionDeclaration labels variables isCertain context funcDecl in 
		value, sideEffects, variables, AFunctionDeclaration(stmt) *) (* Modif #1 *)

	and inline_statement labels declIdents variables isCertain context stmt = 
		let value, calls, sideEffects, declIdents, variables, stmt = match stmt with
		| ABlock block -> let value, sideEffects, declIdents, variables, block = 
			inline_block labels declIdents variables isCertain context block in
			value, StrMap.empty, sideEffects, declIdents, variables, (ABlock block)
		| AVariableStatement stmt -> let value, calls, sideEffects, declIdents, variables,stmt = 
			inline_variableStatement declIdents variables isCertain context stmt 
			in value, calls, sideEffects, declIdents, variables, (AVariableStatement stmt)
		| AConstStatement stmt -> let value, calls, sideEffects, declIdents, variables,stmt = 
			inline_constStatement declIdents variables isCertain context stmt 
			in value, calls, sideEffects, declIdents, variables, (AConstStatement stmt)
		| AFuncDeclaration funcDecl -> let value, sideEffects, declIdents, variables, funcDecl = 
			inline_functionDeclaration declIdents variables isCertain context funcDecl 
			in value, StrMap.empty, sideEffects, declIdents, variables, (AFuncDeclaration funcDecl)	(* Modif #1 *)
		| AEmptyStatement -> {
			returns = [];
			returnsAlways = false;
			throws = false;
			breaks = [];
			anonymousBreak = false;
			}, StrMap.empty, [], declIdents, variables, AEmptyStatement
		| AExpressionStatement(stmt) -> inline_expressionStatement declIdents variables isCertain context stmt
		| AIfStatement(stmt) -> inline_ifStatement labels declIdents variables isCertain context stmt
		| AIterationStatement(stmt) ->
			let return, calls, sideEffects, declIdents, variables, stmt = inline_iterationStatement labels declIdents variables isCertain context stmt in
			let return = {
					returns = return.returns;
					returnsAlways = return.returnsAlways;
					throws = return.throws;
					breaks = return.breaks;
					anonymousBreak = false
			} in 
			return, calls, sideEffects, declIdents, variables, stmt
		| AContinueStatement(stmt) -> {
			returns = [];
			returnsAlways = false;
			throws = false;
			breaks = (match stmt with | Some ident -> [ident] | None -> []);
			anonymousBreak = (stmt = None);
			}, StrMap.empty, [], declIdents, variables , AContinueStatement(stmt)
		| ABreakStatement(stmt) -> {
			returns = [];
			returnsAlways = false;
			throws = false;
			breaks = (match stmt with | Some ident -> [ident] | None -> []);
			anonymousBreak = (stmt = None);
			}, StrMap.empty, [], declIdents, variables, ABreakStatement(stmt)
		| AReturnStatement(stmt) -> inline_returnStatement declIdents variables isCertain context stmt
		| AWithStatement(stmt) -> inline_withStatement labels declIdents variables isCertain context stmt
		| ALabelledStatement(stmt) -> inline_labelledStatement labels declIdents variables isCertain context stmt
		| ASwitchStatement(stmt) -> inline_switchStatement labels declIdents variables isCertain context stmt
		| AThrowStatement(stmt) -> inline_throwStatement declIdents variables isCertain context stmt
		| ATryStatement(stmt) -> inline_tryStatement labels declIdents variables isCertain context stmt
		| ADebuggerStatement -> {
			returns = [];
			returnsAlways = false;
			throws = false;
			breaks = [];
			anonymousBreak = false;
			}, StrMap.empty, [], declIdents, variables, ADebuggerStatement
		in
		let declIdents, stmt = add_calls_to_stmt declIdents calls stmt in
		value, sideEffects, declIdents, variables, stmt

	and inline_block labels declIdents variables isCertain context stmts = 
		inline_list (inline_statement labels) declIdents variables isCertain context stmts

	and inline_variableStatement declIdents variables isCertain context stmt =
		inline_variableDeclarations true declIdents variables isCertain context stmt

	and inline_constStatement declIdents variables isCertain context stmt =
		inline_variableDeclarations false declIdents variables isCertain context stmt

	and inline_returnStatement declIdents variables isCertain context = function
		| None -> {
				returns = [VUndefined];
				returnsAlways = true;
				throws = false;
				breaks = [];
				anonymousBreak = false;
			}, StrMap.empty, [], declIdents, variables, (AReturnStatement None)
		| Some expr -> let calls, exprValue, _, sideEffects, declIdents, variables, expr = inline_expression true declIdents variables isCertain context expr in
			{
				returns = [exprValue];
				returnsAlways = true;
				throws = false;
				breaks = [];
				anonymousBreak = false;
			}, calls, sideEffects, declIdents, variables, (AReturnStatement (Some expr))

	and inline_withStatement labels declIdents variables isCertain context (expr,stmt) =
		let calls,_,_,sideEffects1,declIdents,variables,expr = inline_expression true declIdents variables isCertain context expr in
		let value, sideEffects2, declIdents, variables, stmt = inline_statement labels declIdents variables isCertain context stmt in
			value, calls, sideEffects1@sideEffects2, declIdents, variables, AWithStatement(expr,stmt)

	and inline_labelledStatement labels declIdents variables isCertain context (ident,stmt) =
		let return, sideEffects, declIdents, variables, stmt = inline_statement (StrSet.add ident labels) declIdents variables isCertain context stmt in
		{
				returns = return.returns;
				returnsAlways = return.returnsAlways;
				throws = return.throws;
				breaks = List.filter (fun i -> i <> ident) return.breaks;
				anonymousBreak = return.anonymousBreak;
		}, StrMap.empty, sideEffects, declIdents, variables, ALabelledStatement(ident,stmt)

	and inline_throwStatement declIdents variables isCertain context expr =
		let calls, value,_,sideEffects, declIdents, variables, expr = inline_expression true declIdents variables isCertain context expr in
		{
			returns = [];
			returnsAlways = false;
			throws = true;
			breaks = [];
			anonymousBreak = false;
		}, calls, sideEffects, declIdents, variables, (AThrowStatement expr)

	and inline_tryStatement labels declIdents variables isCertain context = function
		| ATryCatch(block, catch) -> let return1, sideEffects1, declIdents, variables, block = inline_block labels declIdents variables isCertain context block in
			let return2, sideEffects2, declIdents, variables, catch = inline_catch labels declIdents variables false context catch in
			let variables = erase_sideEffects variables isCertain context (sideEffects1@sideEffects2) in
			{
				returns = if return1.throws then return2.returns else return1.returns@return2.returns;
				returnsAlways = if return1.throws then return2.returnsAlways else return1.returnsAlways && return2.returnsAlways;
				throws = return1.throws && return2.throws;
				breaks = [];				(* Could be discussed. If there is a catch, there might not append break *)
				anonymousBreak = false;		(* Could be discussed *)
			}, StrMap.empty, sideEffects1@sideEffects2, declIdents, variables, ATryStatement(ATryCatch(block,catch))
		| ATryFinally(block, finally) -> let return1, sideEffects1, declIdents, variables, block = inline_block labels declIdents variables isCertain context block in
			let return2, sideEffects2, declIdents, variables, finally = inline_finally labels declIdents variables isCertain context finally in
			let variables = erase_sideEffects variables isCertain context sideEffects1 in
			{
				returns = if return2.returnsAlways then return2.returns else return1.returns@return2.returns;
				returnsAlways = return1.returnsAlways || return2.returnsAlways;
				throws = return1.throws || return2.throws;
				breaks = if (return2.breaks <> []) || return2.anonymousBreak then return2.breaks  else return1.breaks;
				anonymousBreak = (return1.anonymousBreak && (return2.breaks = [])) || return2.anonymousBreak
			
			}, StrMap.empty, sideEffects1@sideEffects2, declIdents, variables, ATryStatement(ATryFinally(block,finally))
		| ATryCatchFinally(block, catch, finally ) -> 
			let return1, sideEffects1, declIdents, variables, block = inline_block labels declIdents variables isCertain context block in
			let return2, sideEffects2, declIdents, variables, catch = inline_catch labels declIdents variables false context catch in
			let sideEffects = sideEffects1@sideEffects2 in
			let variables = erase_sideEffects variables isCertain context sideEffects in
			let return3, sideEffects3, declIdents, variables, finally = inline_finally labels declIdents variables isCertain context finally in
			{
				returns = if return3.returnsAlways then return3.returns else (if return1.throws then return2.returns else return1.returns@return2.returns)@return3.returns;
				returnsAlways = (if return1.throws then return2.returnsAlways else return1.returnsAlways && return2.returnsAlways) || return3.returnsAlways;
				throws = (return1.throws && return2.throws) || return3.throws;
				breaks = if (return3.breaks <> []) || return3.anonymousBreak then return3.breaks  else return1.breaks@return2.breaks;
				anonymousBreak = (return1.anonymousBreak && return2.anonymousBreak && (return3.breaks = [])) || return3.anonymousBreak
			}, StrMap.empty, sideEffects@sideEffects3, declIdents, variables, ATryStatement(ATryCatchFinally(block,catch,finally))

	and inline_catch labels declIdents variables isCertain context (ident,block) =
		let variables = StrMap.add ident (VarVar VUnknown) variables in
		let return, sideEffects, declIdents, variables, block = inline_block labels declIdents variables isCertain context block in
			return, sideEffects, declIdents, variables, (ident,block)

	and inline_finally labels declIdents variables isCertain context block =
		inline_block labels declIdents variables isCertain context block
		
	and test_find_caseClauses variables isCertain context switchValue = function
		| [] -> Some false
		| caseClause::caseClauses -> 
			let expr, _ = caseClause in
			let _,exprValue, _,sideEffects1, _, variables, expr = inline_expression false (StrSet.empty,StrSet.empty,StrSet.empty) variables isCertain context expr in
			(match switchValue with
				| VUnknownButTrue | VUnknownButFalse | VUnknown -> None
				| _ -> (match exprValue with
					| VUnknownButTrue | VUnknownButFalse | VUnknown -> None
					| _ -> 
						(match merge_types exprValue switchValue with
							| VUnknownButTrue | VUnknownButFalse | VUnknown -> 
								test_find_caseClauses variables isCertain context switchValue caseClauses
							| _ -> (Some true)
						)
					)
			)

	and inline_switchStatement labels declIdents variables isCertain context (expr,caseBlock) =
		let calls, switchValue,_,sideEffects1, declIdents, variables, expr = inline_expression true declIdents variables isCertain context expr in
		let return, sideEffects2, declIdents, variables, _ = inline_caseBlock labels declIdents variables isCertain context switchValue caseBlock in
		{
			returns = return.returns;
			returnsAlways = return.returnsAlways;
			throws = return.throws;
			breaks = return.breaks;
			anonymousBreak = false
		}, calls, sideEffects1@sideEffects2, declIdents, variables, ASwitchStatement(expr,caseBlock)

	and inline_caseBlock labels declIdents variables isCertain context switchValue = function
		| ACasesNoDefault(caseClauses) -> 
			let find = test_find_caseClauses variables isCertain context switchValue caseClauses in
			(match find with
				| None ->
					let _, return, sideEffects, declIdents, variables = inline_caseClauses labels declIdents variables false context switchValue false caseClauses in
						return, sideEffects, declIdents, variables, AEmptyStatement
				| Some false -> defaultReturn, [], declIdents, variables, AEmptyStatement
				| Some true -> let _, return, sideEffects, declIdents, variables = inline_caseClausesAsStmt labels declIdents variables isCertain context switchValue caseClauses in
				return, sideEffects, declIdents, variables, AEmptyStatement)
				
		| ACasesDefault(caseClauses1, defaultClause, caseClauses2) -> 

			let find = test_find_caseClauses variables isCertain context switchValue caseClauses1 in
			let find = (match find with
				| None -> None
				| Some true -> Some true
				| Some false -> test_find_caseClauses variables isCertain context switchValue caseClauses2
				) in
			(match find with
				| None -> let found, return1, sideEffects1, declIdents, variables = inline_caseClauses labels declIdents variables false context switchValue false caseClauses1 in
					let return2, sideEffects2, declIdents, variables, defaultClause = inline_defaultClause labels declIdents variables false context defaultClause in
					let variables = erase_sideEffects variables isCertain context sideEffects2 in
					let found, return3, sideEffects3, declIdents, variables = inline_caseClauses labels declIdents variables false context switchValue true caseClauses2 in
					let fullSideEffects = sideEffects1@sideEffects2@sideEffects3 in
					(*let variables = if found then variables else erase_sideEffects variables isCertain context fullSideEffects in*)
					{
						returns = return1.returns@return2.returns@return3.returns;
						returnsAlways = return1.returnsAlways && return2.returnsAlways && return3.returnsAlways;
						throws = return1.throws && return2.throws && return3.throws;
						breaks = list_intersect (return1.breaks::return2.breaks::return3.breaks::[]);
						anonymousBreak = false
					}, fullSideEffects, declIdents, variables, AEmptyStatement
				| Some false ->
					let _, _, sideEffects, declIdents, variables = inline_caseClausesAsStmt labels declIdents variables isCertain context switchValue caseClauses1 in 
					let stmts = defaultClause @ (List.fold_left (fun lst (_,stmts) -> lst@stmts) [] caseClauses2) in 
					let return1, sideEffects1, declIdents, variables, stmts = 
						inline_list (inline_statement labels) declIdents variables isCertain context stmts in
					return1, sideEffects1, declIdents, variables, AEmptyStatement
				| Some true -> 
					let found, _, sideEffects1, declIdents, variables = inline_caseClausesAsStmt labels declIdents variables isCertain context switchValue caseClauses1 in 
					if found then
						let stmts2 = defaultClause @ (List.fold_left (fun lst (_,stmts) -> lst@stmts) [] caseClauses2) in 
						let return2, sideEffects2, declIdents, variables, stmts2 = 
							inline_list (inline_statement labels) declIdents variables isCertain context stmts2 in
						return2, (sideEffects1@sideEffects2), declIdents, variables, AEmptyStatement
					else
						let _, return2, sideEffects2, declIdents, variables = inline_caseClausesAsStmt labels declIdents variables isCertain context switchValue caseClauses2 in 
						return2, (sideEffects1@sideEffects2), declIdents, variables, AEmptyStatement
			)
			
	and inline_caseClausesAsStmt labels declIdents variables isCertain context switchValue = function
		| [] -> false, defaultReturn, [], declIdents, variables
		| caseClause::caseClauses -> let found = test_find_caseClauses variables isCertain context switchValue [caseClause] in
			let expr, _ = caseClause in
			let _, _, _,sideEffects1, declIdents, variables, expr = inline_expression false declIdents variables isCertain context expr in
			let found, return2, sideEffects2, declIdents, variables = (match found with
				| None -> false, defaultReturn, [], declIdents, variables (* Should never happen *)
				| Some false -> inline_caseClausesAsStmt labels declIdents variables isCertain context switchValue caseClauses
				| Some true -> let (stmts:statement list) = List.fold_left (fun lst (_,stmts) -> lst@stmts) [] (caseClause::caseClauses) in
					let return2, sideEffects2, declIdents, variables, stmts = 
						inline_list (inline_statement labels) declIdents variables isCertain context stmts in
					true, return2, sideEffects2, declIdents, variables) in
			found, return2, sideEffects1@sideEffects2, declIdents, variables

	and inline_caseClauses labels declIdents variables isCertain context switchValue found = function
		| [] -> found, defaultReturn, [], declIdents, variables
		| caseClause::caseClauses -> let foundInClause, return1, sideEffects1, declIdents, variables = 
			inline_caseClause labels declIdents variables isCertain context switchValue found caseClause in
			let found, return2, sideEffects2, declIdents, variables = inline_caseClauses labels declIdents variables isCertain context switchValue foundInClause caseClauses in
			let variables = if foundInClause then erase_sideEffects variables isCertain context sideEffects1 else variables in
			found, {
				returns = return1.returns@return2.returns;
				returnsAlways = return1.returnsAlways && return2.returnsAlways;
				throws = return1.throws && return2.throws;
				breaks = list_intersect (return1.breaks::return2.breaks::[]);
				anonymousBreak = return1.anonymousBreak && return2.anonymousBreak
			}, sideEffects1@sideEffects2, declIdents, variables

	and inline_caseClause labels declIdents variables isCertain context switchValue found (expr,stmts) =
		let _, exprValue, _,sideEffects1, declIdents, variables, _ = inline_expression false declIdents variables isCertain context expr in
		if found then
			let return2, sideEffects2, declIdents, variables, stmts = 
				inline_list (inline_statement labels) declIdents variables isCertain context stmts in
				found, return2, sideEffects1@sideEffects2, declIdents, variables
		else
			let found = test_find_caseClauses variables isCertain context switchValue [(expr,stmts)] in
			(match found with
				| None -> let return2, sideEffects2, declIdents, variables, stmts = 
					inline_list (inline_statement labels) declIdents variables isCertain context stmts in
					true, return2, sideEffects1@sideEffects2, declIdents, variables
				| Some _ -> false, defaultReturn, sideEffects1, declIdents, variables
			)

	and inline_defaultClause labels declIdents variables isCertain context stmts =
		inline_list (inline_statement labels) declIdents variables isCertain context stmts

	and inline_variableDeclarations isvar declIdents variables isCertain context (is_private,is_meta,stmt) = match stmt with
		| [] -> defaultReturn, StrMap.empty, [], declIdents, variables, (is_private,is_meta,[])
		| (ident,init)::lst ->
			let calls, sideEffects, declIdents, variables, decl = (match init with
				| None -> let variables = StrMap.add ident (if isvar then (VarVar VUndefined) else (VarConst VUndefined)) variables in
					StrMap.empty, [], declIdents, variables,(ident,None)
				| Some init -> let calls, v, sideEffects, declIdents, variables, init = inline_initializer declIdents variables isCertain context init in
					let variables = if context.co_nesting = 0 then (ignore(set_value variables isCertain context v (VString ident)); variables)
					else StrMap.add ident (if isvar then (VarVar v) else (VarConst v)) variables in
					calls, sideEffects, declIdents, variables,(ident,Some init)
			) in
			let _, calls2, sideEffects2, declIdents, variables, (_,_,lst) = inline_variableDeclarations isvar declIdents variables isCertain context (is_private,is_meta,lst) in
			defaultReturn, (StrMap.fold (fun k v calls -> StrMap.add k v calls) calls2 calls), sideEffects@sideEffects2, declIdents, variables, (is_private,is_meta,decl::lst)

	and inline_initializer declIdents variables isCertain context assignExpr =
		let calls, exprValue, _, sideEffects, declIdents, variables, assignExpr = 
			inline_assignmentExpression true declIdents variables isCertain context assignExpr in
		calls, exprValue, sideEffects, declIdents, variables, assignExpr

	and inline_functionDeclaration declIdents variables isCertain context (is_private, is_meta, is_inline, ident,params,body) =
		let index = create_function is_inline params body true in
		let variables = StrMap.add ident (VarVar(VMemory index)) variables in
		let _, _, body = inline_functionContent declIdents variables isCertain {
				co_current = None;
				co_context = context.co_context;
				co_nesting = context.co_nesting
			} params body in
			defaultReturn, [], declIdents, variables,(is_private, is_meta, is_inline, ident,params,body)

	and inline_functionExpression declIdents variables isCertain context (is_inline, ident,params,body) =
		let index = create_function is_inline params body true in
		let _, _, body = (match ident with
			| None ->
				inline_functionContent declIdents variables isCertain {
				co_current = None;
				co_context = context.co_context;
				co_nesting = context.co_nesting
			} params body
			| Some ident ->
				let variables = StrMap.add ident (VarVar (VMemory index)) variables in
				inline_functionContent declIdents variables isCertain {
					co_current = None;
					co_context = context.co_context;
					co_nesting = context.co_nesting
				} params body)
			in (VMemory(index)),defaultRefValue,[],declIdents, variables,(is_inline, ident,params,body)

	and inline_functionContent (declIdents,labels,absolute) variables isCertain context params body =
		let memory = memory_reinstall () in
		let variables = StrMap.fold (fun key value map -> match value with
			| VarConst _ -> StrMap.add key value map
			| VarVar _ -> StrMap.add key (VarVar VUnknown) map) variables StrMap.empty in
		let variables = List.fold_left (fun variables ident -> StrMap.add ident (VarVar VUnknown) variables) variables params in

		let fullfill = fullfill_list fullfill_sourceElement {
			idents = List.fold_right (fun i set -> StrSet.add i set ) params declIdents;
			labels = labels;
			locals = StrSet.empty;
			} body in
		let absolute = absolute_list absolute_sourceElement {
			abs_labels = StrSet.empty;
			abs_idents = StrSet.empty
		} fullfill.idents body in
		
		let declIdents = fullfill.idents, absolute.abs_labels, absolute.abs_idents in
		
		let exprValue, _, body = inline_functionBody declIdents variables context body in
		let _ = memory_reset memory in
		exprValue, [], body

	and inline_realFunctionContent variables isCertain context params body values =
		if context.co_nesting > 100 then VUnknown, [(defaultRefValue,VUnknown)] else
		let values = if (List.length values) < (List.length params) then
			list_fill_right ((List.length params) - (List.length values)) VUndefined values
			else (list_sub (List.length params) values) in
		let variables = List.fold_left2 (fun variables param value -> StrMap.add param (VarVar value) variables) variables params values in
		let value, sideEffects,  _ = inline_functionBody (StrSet.empty,StrSet.empty,StrSet.empty) variables context body in
		value, sideEffects

	and inline_functionBody (declIdents,labels,absolute) variables context body =
		let fullfill = fullfill_list fullfill_sourceElement {
			idents = declIdents;
			labels = StrSet.empty;
			locals = StrSet.empty
			} body in
		let absolute = absolute_list absolute_sourceElement {
			abs_idents = StrSet.empty;
			abs_labels = StrSet.empty
			} fullfill.idents body in
			
		let effectable = effectable_list effectable_sourceElement StrSet.empty StrSet.empty body in

		let old_effectable = effectable_set effectable in
		
		let context = {
			co_context = (match context.co_current with | None -> 0 | Some e -> e);
			co_current = None;
			co_nesting = context.co_nesting+1
		} in
		
		let functions = Ast_tools.funcDecls_to_map body in
		let declIdents = declIdents, labels, absolute.abs_idents in
		let variables = StrSet.fold (fun e variables -> StrMap.add e (VarVar VUndefined) variables) fullfill.idents variables in
		let variables = StrMap.fold (fun ident (is_inline,params,body) -> 
			let index = create_function is_inline params body true in
			StrMap.add ident (VarVar (VMemory index))) functions variables in
		let return, sideEffects, _, _, body = inline_list (inline_sourceElement StrSet.empty) declIdents variables false context body in 
		let exprValue = if return.returnsAlways then merge_types_list return.returns else merge_types_list (VUndefined::return.returns) in
		
		let _ = effectable_set old_effectable in
		
		exprValue,sideEffects,body

	and inline_expressionStatement declIdents variables isCertain context stmt =
		let calls, _, _, sideEffects, declIdents, variables,stmt = inline_expression true declIdents variables isCertain context stmt in
		defaultReturn, calls, sideEffects, declIdents, variables, (AExpressionStatement stmt)

	and inline_ifStatement labels declIdents variables isCertain context = function
		| AIfElse(expr,stmt1,stmt2) ->
			let calls, exprValue, _, sideEffects, declIdents, variables, expr = inline_expression true declIdents variables isCertain context expr in
			(match exprValue with
				| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse -> 
				let return2, sideEffects2, declIdents, variables, stmt2 = inline_statement labels declIdents variables isCertain context stmt2 in
				let stmt1 = stmt1 in
					return2, calls, sideEffects@sideEffects2, declIdents, variables,(AIfStatement(
						AIfElse(expr,stmt1, stmt2)))
				| VBool true | VString _ | VNumber _ | VMemory _ | VUnknownButTrue -> 
				let return1, sideEffects1, declIdents, variables, stmt1 = inline_statement labels declIdents variables isCertain context stmt1 in
					let stmt2 = stmt2 in
					return1, calls, sideEffects@sideEffects1, declIdents, variables,(AIfStatement(AIfElse(expr,stmt1,stmt2)))
				| _ -> let return1, sideEffects1, declIdents, variables, stmt1 = inline_statement labels declIdents variables false context stmt1 in
					let return2, sideEffects2, declIdents, variables, stmt2 = inline_statement labels declIdents variables false context stmt2 in
					let variables = erase_sideEffects variables isCertain context (sideEffects1@sideEffects2) in
						{
							returns = return1.returns@return2.returns;
							returnsAlways = return1.returnsAlways && return2.returnsAlways;
							throws = return1.throws && return2.throws;
							breaks = list_intersect (return1.breaks::return2.breaks::[]);
							anonymousBreak = return1.anonymousBreak && return2.anonymousBreak
						}, calls, sideEffects@sideEffects1@sideEffects2, declIdents, variables, (AIfStatement(AIfElse(expr, stmt1, stmt2))))
		| AIf(expr,stmt) ->
			let calls, exprValue, _, sideEffects, declIdents, variables, expr = inline_expression true declIdents variables isCertain context expr in
			(match exprValue with
				| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse ->
					let stmt = stmt in
					defaultReturn, calls, [], declIdents, variables, (AIfStatement(AIf(expr,stmt)))
				| VBool true | VString _ | VNumber _ | VMemory _ | VUnknownButTrue -> 
					let return1, sideEffects1, declIdents, variables, stmt = inline_statement labels declIdents variables isCertain context stmt in
					return1, calls, sideEffects@sideEffects1, declIdents, variables,(AIfStatement(AIf(expr,stmt)))
				| _ -> 
					let return1, sideEffects1, declIdents, variables,stmt = inline_statement labels declIdents variables false context stmt in
					let return1 = willMaybeReturn return1 in
					let variables = erase_sideEffects variables isCertain context sideEffects1 in
					return1, calls, sideEffects@sideEffects1, declIdents, variables,(AIfStatement(AIf(expr,stmt))))

	and inline_iterationStatement labels declIdents variables isCertain context = function
		| ADoWhile(stmt,expr) -> 
			let return1, sideEffects1, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
			let calls, exprValue, _, sideEffects, declIdents, variables,expr = inline_expression true declIdents variables false context expr in
			(match exprValue with
				| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse -> 
				return1, calls, sideEffects1@sideEffects, declIdents, variables,(AIterationStatement(ADoWhile(stmt,expr)))
				| _ -> 
				let variables = erase_sideEffects variables isCertain context sideEffects1 in
				return1, calls, sideEffects1@sideEffects, declIdents, variables,(AIterationStatement(ADoWhile(stmt,expr))))
		
		| AWhile(expr,stmt) ->
			let calls, exprValue, _, sideEffects, declIdents, variables, expr = inline_expression true declIdents variables false context expr in
			(match exprValue with
				| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse -> 
					let stmt = stmt in
					defaultReturn, calls, sideEffects, declIdents, variables,(AIterationStatement(AWhile(expr,stmt)))
				| _ -> let return1, sideEffects1, declIdents, variables,stmt = inline_statement labels declIdents variables false context stmt in
				let variables = erase_sideEffects variables isCertain context sideEffects1 in
					return1, calls, sideEffects@sideEffects1, declIdents, variables,(AIterationStatement(AWhile(expr,stmt))))
		| AFor(expr1,expr2,expr3,stmt) ->
			(match expr1 with
				| None -> 
					(match expr2 with
						| None -> 
							(match expr3 with
								| None -> let return4, sideEffects4, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
									let variables = erase_sideEffects variables isCertain context sideEffects4 in
									return4, StrMap.empty, sideEffects4, declIdents, variables,(AIterationStatement(AFor(None,None,None,stmt)))
								| Some expr3 -> let _,_,_,sideEffects3, declIdents, variables, expr3 = inline_expression false declIdents variables false context expr3 in
									let return4, sideEffects4, declIdents, variables, stmt = inline_statement labels declIdents variables isCertain context stmt in
									let variables = erase_sideEffects variables isCertain context (sideEffects3@sideEffects4) in
									return4, StrMap.empty, sideEffects3@sideEffects4, declIdents, variables,(AIterationStatement(AFor(None,None,Some(expr3),stmt))))
						| Some expr2 ->
							let _, exprValue, _, sideEffects, declIdents, variables, expr2 = inline_expression false declIdents variables false context expr2 in
							let sideEffects3, declIdents, variables, expr3 = (match expr3 with
								| None -> [], declIdents, variables, None
								| Some expr3 -> let _, _, _, sideEffects3, declIdents, variables, expr3 = inline_expression false declIdents variables false context expr3 in
									sideEffects3, declIdents, variables, (Some expr3)) in
									(match exprValue with
										| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse -> 
										let stmt = stmt in
										defaultReturn, StrMap.empty, sideEffects, declIdents, variables,(AIterationStatement(AFor(None,Some(expr2),expr3,stmt)))
										| _ -> let return4, sideEffects4, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
										let variables = erase_sideEffects variables isCertain context (sideEffects@sideEffects4) in
									return4, StrMap.empty, sideEffects@sideEffects3@sideEffects4, declIdents, variables,
									(AIterationStatement(AFor(None,Some(expr2),expr3,stmt)))))
								
				| Some expr1 ->
					let calls, _, _, sideEffects1,declIdents,variables,expr1 = inline_expression true declIdents variables isCertain context expr1 in
					(match expr2 with
					| None -> 
						(match expr3 with
							| None -> 
								let sideEffects3, declIdents, variables, expr3 = (match expr3 with
								| None -> [], declIdents, variables, None
								| Some expr3 -> let _, _, _, sideEffects3, declIdents, variables, expr3 = inline_expression false declIdents variables false context expr3 in
									sideEffects3, declIdents, variables, (Some expr3)) in
								let return4, sideEffects4, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
								let variables = erase_sideEffects variables isCertain context (sideEffects3@sideEffects4) in
								return4, calls, sideEffects1@sideEffects3@sideEffects4, declIdents, variables,(AIterationStatement(AFor(Some(expr1),None,expr3,stmt)))
							| Some expr3 -> let _,_,_,sideEffects3,declIdents,variables,expr3 = inline_expression false declIdents variables false context expr3 in
							let return4, sideEffects4, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
								let variables = erase_sideEffects variables isCertain context (sideEffects3@sideEffects4) in
								return4, calls, sideEffects1@sideEffects3@sideEffects4, declIdents, variables,(AIterationStatement(AFor(Some(expr1),None,Some(expr3),stmt))))
					| Some expr2 ->
						let _,exprValue,_,sideEffects,declIdents,variables,expr2 = inline_expression false declIdents variables false context expr2 in
						let sideEffects3, declIdents, variables, expr3 = (match expr3 with
								| None -> [], declIdents, variables, None
								| Some expr3 -> let _, _, _, sideEffects3, declIdents, variables, expr3 = inline_expression false declIdents variables false context expr3 in
									sideEffects3, declIdents, variables, (Some expr3)) in
							(match exprValue with
								| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse ->
								let stmt = stmt in
								defaultReturn, calls, sideEffects1@sideEffects@sideEffects3, declIdents, variables,(AIterationStatement(AFor(Some(expr1),Some(expr2),expr3,stmt)))
								| _ -> let return4, sideEffects4, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
								let sideEffects = sideEffects@sideEffects3@sideEffects4 in
								let variables = erase_sideEffects variables isCertain context sideEffects in
							return4, calls, sideEffects1@sideEffects, declIdents, variables,(AIterationStatement(AFor(Some(expr1),Some(expr2),expr3,stmt))))
							))
		| AForVar(is_meta, varDecl,expr2,expr3,stmt) ->
			let return1, calls, sideEffects1, declIdents, variables, (_,_,varDecl) = inline_variableStatement declIdents variables isCertain context (true,is_meta,varDecl) in
				(match expr2 with
				| None -> 
					(match expr3 with
						| None -> let return2, sideEffects2, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
							let variables = erase_sideEffects variables isCertain context sideEffects2 in
							return2, calls, sideEffects1@sideEffects2, declIdents, variables,(AIterationStatement(AForVar(is_meta,varDecl,None,None,stmt)))
						| Some expr3 -> let _,_,_,sideEffects3,declIdents,variables,expr3 = inline_expression false declIdents variables false context expr3 in
							let return2, sideEffects2, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
							let variables = erase_sideEffects variables isCertain context (sideEffects3@sideEffects2) in
							return2, calls, sideEffects1@sideEffects3@sideEffects2, declIdents, variables,(AIterationStatement(AForVar(is_meta,varDecl,None,Some(expr3),stmt))))
				| Some expr2 ->
					let _, exprValue, _, sideEffects, declIdents, variables,expr2 = inline_expression false declIdents variables false context expr2 in
					let sideEffects3, declIdents, variables, expr3 = (match expr3 with
						| None -> [], declIdents, variables, None
						| Some expr3 -> let _, _, _, sideEffects3, declIdents, variables, expr3 = inline_expression false declIdents variables false context expr3 in
							sideEffects3, declIdents, variables, (Some expr3)) in
							(match exprValue with
								| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse -> 
									let stmt = stmt in
									defaultReturn, calls, sideEffects, declIdents, variables,(AIterationStatement(AForVar(is_meta,varDecl,Some(expr2),expr3,stmt)))
								| _ -> let return2, sideEffects2, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
								let variables = erase_sideEffects variables isCertain context (sideEffects@sideEffects3@sideEffects2) in
							return2, calls, sideEffects1@sideEffects@sideEffects3@sideEffects2, declIdents, variables,(AIterationStatement(AForVar(is_meta,varDecl,Some(expr2),expr3,stmt))))
						)
		| AForIn(lhs,expr,stmt) ->
			let calls, _, refValue, sideEffects1, declIdents, variables, lhs = inline_leftHandSideExpression true declIdents variables isCertain context lhs in
			let _,exprValue, _, sideEffects2, declIdents, variables, expr = inline_expression false declIdents variables isCertain context expr in
				(match exprValue with
					| VBool _ | VString "" | VNumber _ | VUndefined | VNull | VUnknownButFalse -> 
						let stmt = stmt in
						defaultReturn, calls, sideEffects1@sideEffects2, declIdents, variables, (AIterationStatement(AForIn(lhs,expr,stmt)))
					| VMemory(index) -> let exprValue = memory_get index in
						(match exprValue with
							| MArray(map,exact) ->
								(if exact && (map = StrMap.empty) then
								let stmt = stmt in
								defaultReturn, calls, sideEffects1@sideEffects2, declIdents, variables, (AIterationStatement(AForIn(lhs,expr,stmt)))
									else let return3, sideEffects3, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
									let sideEffects = sideEffects1@[(refValue,VUnknown)]@sideEffects2@sideEffects3 in
									let variables = erase_sideEffects variables isCertain context sideEffects in
									return3, calls, sideEffects, declIdents, variables, (AIterationStatement(AForIn(lhs,expr,stmt))))
							| MFunction (_,_,_,map,exact) ->
								if exact && StrMap.is_empty map then
								let stmt = stmt in
								defaultReturn, calls, sideEffects1@sideEffects2, declIdents, variables, (AIterationStatement(AForIn(lhs,expr,stmt)))
								else
								let return3, sideEffects3, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
									let sideEffects = sideEffects1@[(refValue,VUnknown)]@sideEffects2@sideEffects3 in
									let variables = erase_sideEffects variables isCertain context sideEffects in
									return3, calls, sideEffects, declIdents, variables, (AIterationStatement(AForIn(lhs,expr,stmt)))
							| MObject(items,items2,items3,exact) ->
								if exact && StrMap.is_empty items && StrMap.is_empty items2 && StrMap.is_empty items3 then
									let stmt = stmt in
									defaultReturn, calls, sideEffects1@sideEffects2, declIdents, variables,
									(AIterationStatement(AForIn(lhs,expr,stmt)))
								else let return3, sideEffects3, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
									let sideEffects = sideEffects1@[(refValue,VUnknown)]@sideEffects2@sideEffects3 in
									let variables = erase_sideEffects variables isCertain context sideEffects in
									return3, calls, sideEffects, declIdents, variables, (AIterationStatement(AForIn(lhs,expr,stmt)))
							| _ -> let return3, sideEffects3, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
								let sideEffects = sideEffects1@[(refValue,VUnknown)]@sideEffects2@sideEffects3 in
								let variables = erase_sideEffects variables isCertain context sideEffects in
								return3, calls, sideEffects, declIdents, variables, (AIterationStatement(AForIn(lhs,expr,stmt))))
					| _ -> let return3, sideEffects3, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
						let sideEffects = sideEffects1@[(refValue,VUnknown)]@sideEffects2@sideEffects3 in
						let variables = erase_sideEffects variables isCertain context sideEffects in
						return3, calls, sideEffects, declIdents, variables, (AIterationStatement(AForIn(lhs,expr,stmt))))
		| AForVarIn(is_meta,varDecl, expr, stmt) -> 
			let return1, calls, sideEffects1, declIdents, variables, (_,_,varDecl) = inline_variableDeclarations true declIdents variables isCertain context (true,is_meta,[varDecl]) in
			let varDecl = match varDecl with
				| [] -> raise Logic_error
				| a::b -> a in
			let _, exprValue, _, sideEffects, declIdents, variables, expr = inline_expression false declIdents variables isCertain context expr in
				(match exprValue with
					| VBool _  | VString "" | VNumber _ | VUndefined | VNull | VUnknownButFalse -> 
						let stmt = stmt in
						return1, calls, sideEffects1@sideEffects, declIdents, variables, 
						(AIterationStatement(AForVarIn(is_meta,varDecl,expr,stmt)))
					| VMemory(index) -> let exprValue = memory_get index in
						(match exprValue with
							| MArray (map,exact) ->
								(if exact && (map = StrMap.empty) then 
									let stmt = stmt in
									defaultReturn, calls, sideEffects1@sideEffects, declIdents, variables,
										(AIterationStatement(AForVarIn(is_meta,varDecl,expr,stmt)))
								else let return2, sideEffects2, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
									let variables = erase_sideEffects variables isCertain context sideEffects2 in
									return2, calls, sideEffects1@sideEffects@sideEffects2, declIdents, variables, 
										(AIterationStatement(AForVarIn(is_meta,varDecl,expr,stmt))))
							| MFunction (_,_,_,map,exact) ->
								if exact && StrMap.is_empty map then
									let stmt = stmt in
									defaultReturn, calls, sideEffects1@sideEffects, declIdents, variables,
										(AIterationStatement(AForVarIn(is_meta,varDecl,expr,stmt)))
								else
								let return2, sideEffects2, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
									let variables = erase_sideEffects variables isCertain context sideEffects2 in
									return2, calls, sideEffects1@sideEffects@sideEffects2, declIdents, variables,
										(AIterationStatement(AForVarIn(is_meta,varDecl,expr,stmt)))
							| MObject (items1,items2,items3,exact) ->
								if exact && StrMap.is_empty items1 && StrMap.is_empty items2 && StrMap.is_empty items3 then
									let stmt = stmt in
									defaultReturn, calls, sideEffects1@sideEffects, declIdents, variables,
										(AIterationStatement(AForVarIn(is_meta,varDecl,expr,stmt)))
								else let return2, sideEffects2, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
									let variables = erase_sideEffects variables isCertain context sideEffects2 in
									return2, calls, sideEffects1@sideEffects@sideEffects2, declIdents, variables,
										(AIterationStatement(AForVarIn(is_meta,varDecl,expr,stmt)))
							| _ -> let return2, sideEffects2, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
								let variables = erase_sideEffects variables isCertain context sideEffects2 in
								return2, calls, sideEffects1@sideEffects@sideEffects2, declIdents, variables,
									(AIterationStatement(AForVarIn(is_meta,varDecl,expr,stmt))))
					| _ -> let return2, sideEffects2, declIdents, variables, stmt = inline_statement labels declIdents variables false context stmt in
						let variables = erase_sideEffects variables isCertain context sideEffects2 in
						return2, calls, sideEffects1@sideEffects@sideEffects2, declIdents, variables,
							(AIterationStatement(AForVarIn(is_meta,varDecl,expr,stmt))))

	and inline_expression can_inline declIdents variables isCertain context = function
		| [] -> StrMap.empty, VBool(false), defaultRefValue, [], declIdents, variables, []
		| [a] -> let calls, exprValue, refValue, sideEffects, declIdents, variables, a = inline_assignmentExpression can_inline declIdents variables isCertain context a in
			calls, exprValue, refValue, sideEffects, declIdents, variables, [a]
		| a::b -> let calls1, _, _, sideEffects1, declIdents, variables, a = inline_assignmentExpression can_inline declIdents variables isCertain context a in
			let calls2, exprValue, refValue, sideEffects2, declIdents, variables, b = inline_expression can_inline declIdents variables isCertain context b in
				(Tools_structs.merge_maps calls1 calls2), exprValue, refValue, sideEffects1 @ sideEffects2, declIdents, variables, a::b

	and inline_assignmentExpression can_inline declIdents variables isCertain context = function
		| ACondExpr(expr) -> let calls, exprValue, refValue, sideEffects, declIdents, variables, expr = 
				inline_conditionalExpression can_inline declIdents variables isCertain context expr in
			calls, exprValue, refValue, sideEffects, declIdents, variables, ACondExpr(expr)
		| AAssignOp(lhs,op,expr) -> 
			let calls1, _, refValue1, sideEffects1, declIdents, variables, lhs = inline_leftHandSideExpression can_inline declIdents variables isCertain context lhs in
			let calls2, exprValue2, refValue2, sideEffects2, declIdents, variables, expr = inline_assignmentExpression can_inline declIdents variables isCertain context expr in
			let context = (match refValue1.ref_index with
				| Some index -> 
					{
					co_context = context.co_context;
					co_current = Some index;
					co_nesting = context.co_nesting
					}
				| None -> context) in
			let sideEffects, variables = set_value variables isCertain context exprValue2 refValue1.ref_value in
			(Tools_structs.merge_maps calls1 calls2), exprValue2, refValue2, 
				sideEffects1 @ sideEffects2 @ sideEffects @ [(refValue1,exprValue2)], declIdents, variables, AAssignOp(lhs,op,expr)

	and inline_leftHandSideExpression can_inline declIdents variables isCertain context = function
		| ANewExpression(expr) -> let calls, exprValue, refValue, sideEffects, declIdents, variables, expr = 
				inline_newExpression can_inline declIdents variables isCertain context expr in
			calls, exprValue, refValue, sideEffects, declIdents, variables, ANewExpression(expr)
		| ACallExpression(expr) ->	let calls, exprValue, refValue, sideEffects, declIdents, variables, expr = 
				inline_callExpression can_inline declIdents variables isCertain context expr in
			calls, exprValue, refValue, sideEffects, declIdents, variables, expr

	and inline_newExpression can_inline declIdents variables isCertain context = function
		| AMemberExpression(expr) -> let calls, exprValue, refValue, _, sideEffects, declIdents, variables, expr = 
				inline_memberExpression can_inline declIdents variables isCertain true context expr in
			calls, exprValue, refValue, sideEffects, declIdents, variables, AMemberExpression(expr)
		| ANewMemberExpression(expr) -> 
			let calls, exprValue, _, sideEffects1, declIdents, variables, expr = inline_newExpression true declIdents variables isCertain context expr in
			let newIndex = memory_push (MObject((StrMap.add "constructor" exprValue StrMap.empty),StrMap.empty,StrMap.empty,true)) in
			(match exprValue with
				| VMemory index -> 
					(match memory_get index with
						| MFunction (_,params,body,_,_) -> 
							let context = {
								co_context = context.co_context;
								co_current = Some newIndex;
								co_nesting = context.co_nesting
							} in
							let _, sideEffects = inline_realFunctionContent variables isCertain context params body [] in
							calls, VMemory(newIndex), defaultRefValue, sideEffects1@sideEffects, declIdents, variables, ANewMemberExpression(expr)
						| _ -> calls, VMemory(newIndex), defaultRefValue, sideEffects1@[(defaultRefValue,VUnknown)], declIdents, variables, ANewMemberExpression(expr)
					)
				| _ -> calls, VMemory(newIndex), defaultRefValue, sideEffects1@[(defaultRefValue,VUnknown)], declIdents, variables, ANewMemberExpression(expr)
			)
			
	(* TODO: Jusqu'ici, ça  va *)
	and inline_callExpression can_inline declIdents variables isCertain context = function
		
		| ACallMemberArgs(
			AMemberDotExpr(expr, "call"), args ) -> let calls1, exprValue, refValue, context, sideEffects1, declIdents, variables, expr 
			= inline_memberExpression can_inline declIdents variables isCertain true context expr in
			let calls2, values, sideEffects2, declIdents, variables, args = inline_arguments can_inline declIdents variables isCertain context args in
			let can_inline = can_inline && (sideEffects2 = []) in
			let calls = Tools_structs.merge_maps calls1 calls2 in
			let defaultAst = ACallExpression(ACallMemberArgs(AMemberDotExpr (expr, "call"),args)) in
			let sideEffects = sideEffects1@sideEffects2@[defaultRefValue,VUnknown] in
			(match exprValue with
				| VMemory index -> 
					(match memory_get index with
						| MFunction (is_inline,params,body,_,_) -> 
							(match values with
								| [] -> let variables = erase_sideEffects variables isCertain context sideEffects in
									calls, VUnknown, defaultRefValue, sideEffects, declIdents, variables, defaultAst
								| a::values -> 
									(match a with
										| VMemory index2 -> 
											let context = {
												co_context = index2;
												co_current = Some index2;
												co_nesting = context.co_nesting
											} in
											let value, sideEffects = inline_realFunctionContent variables isCertain context params body values in
											if is_inline && can_inline then
												let declIdents, labels, absolute = declIdents in
												let map,declIdents,_ = Tools_identifier.free_ident (StrMap.empty,declIdents, absolute) "?" false in
												let ident = StrMap.find "?" map in
												let refPath = (match args with
													| [] -> APrimaryExpr (AIdentifier "window")
													| a::_ -> APrimaryExpr (AExpression [a])) in
												let calls = StrMap.add ident (refPath,params,args,body) calls in
												calls, VUnknown, defaultRefValue, sideEffects1@sideEffects2@[(defaultRefValue,VUnknown)], (declIdents,labels,absolute) , variables, ANewExpression(
													AMemberExpression(APrimaryExpr(AIdentifier(ident)))
												)
											else
								
												calls, value, defaultRefValue, sideEffects1@sideEffects2@sideEffects, declIdents, variables, defaultAst
										| _ -> let variables = erase_sideEffects variables isCertain context sideEffects in
										calls, VUnknown, defaultRefValue, sideEffects, declIdents, variables, defaultAst
									)
							)
						| _ -> let variables = erase_sideEffects variables isCertain context sideEffects in
							calls, VUnknown, defaultRefValue, sideEffects, declIdents, variables, defaultAst
					)
				| _ -> let variables = erase_sideEffects variables isCertain context sideEffects in
					calls, VUnknown, defaultRefValue, sideEffects, declIdents, variables, defaultAst
			)
		
		| ACallMemberArgs(
			AMemberDotExpr(expr, "apply"), args ) -> let calls1, exprValue, refValue, context, sideEffects1, declIdents, variables, expr 
			= inline_memberExpression can_inline declIdents variables isCertain true context expr in
			let calls2, values, sideEffects2, declIdents, variables, args = inline_arguments can_inline declIdents variables isCertain context args in
			let can_inline = can_inline && (sideEffects2 = []) in
			let calls = Tools_structs.merge_maps calls1 calls2 in
			let defaultAst = ACallExpression(ACallMemberArgs(AMemberDotExpr (expr, "apply"),args)) in
			let sideEffects = sideEffects1@sideEffects2@[defaultRefValue,VUnknown] in
			(match exprValue with
				| VMemory index -> 
					(match memory_get index with
						| MFunction (is_inline,params,body,_,_) -> 
							(match values with
								| a::lst ->
									let values = (match lst with
										| [] -> Some []
										| b::_ -> (match b with
											| (VMemory index3) -> 
												(match memory_get index3 with
													| MArray (values,_) -> 
														Some (jsArray_to_list values)
													| _ -> None
												)
											| _ -> None
										)) in
									(match a, values with
										| (VMemory index2), (Some values) ->
											
											let context = {
												co_context = index2;
												co_current = Some index2;
												co_nesting = context.co_nesting
											} in
											let value, sideEffects = inline_realFunctionContent variables isCertain context params body values in
											if is_inline && can_inline then
												let declIdents, labels, absolute = declIdents in
												let map,declIdents,_ = Tools_identifier.free_ident (StrMap.empty,declIdents, absolute) "?" false in
												let ident = StrMap.find "?" map in
												let refPath = (match args with
													| [] -> APrimaryExpr (AIdentifier "window")
													| a::_ -> APrimaryExpr (AExpression [a])) in
												let calls = StrMap.add ident (refPath,params,args,body) calls in
												calls, VUnknown, defaultRefValue, sideEffects1@sideEffects2@[(defaultRefValue,VUnknown)], (declIdents,labels,absolute) , variables, ANewExpression(
													AMemberExpression(APrimaryExpr(AIdentifier(ident)))
												)
											else
					
												calls, value, defaultRefValue, sideEffects1@sideEffects2@sideEffects, declIdents, variables, defaultAst
											
										| _ -> 
											let variables = erase_sideEffects variables isCertain context sideEffects in
											calls, VUnknown, defaultRefValue, sideEffects, declIdents, variables, defaultAst
									)
								| _ -> let variables = erase_sideEffects variables isCertain context sideEffects in
									calls, VUnknown, defaultRefValue, sideEffects, declIdents, variables, defaultAst
							)
						| _ -> let variables = erase_sideEffects variables isCertain context sideEffects in
								calls, VUnknown, defaultRefValue, sideEffects, declIdents, variables, defaultAst
					)
				| _ -> let variables = erase_sideEffects variables isCertain context sideEffects in
						calls, VUnknown, defaultRefValue, sideEffects, declIdents, variables, defaultAst
			)
	
	
	
		| ACallMemberArgs(expr,args) -> 
			let calls1, exprValue, refValue, context, sideEffects1, declIdents, variables, expr 
				= inline_memberExpression can_inline declIdents variables isCertain true context expr in
			let calls2, values, sideEffects2, declIdents, variables, args 
				= inline_arguments can_inline declIdents variables isCertain context args in
			let can_inline = can_inline && (sideEffects2 = []) in
			let calls = Tools_structs.merge_maps calls1 calls2 in
			(match exprValue with
				| VMemory index -> 
					(match memory_get index with
						| MFunction (is_inline,params,body,_,_) -> 
						let value, sideEffects = inline_realFunctionContent variables isCertain context params body values in
							if is_inline && can_inline then
								let declIdents, labels, absolute = declIdents in
								let map,declIdents,_ = Tools_identifier.free_ident (StrMap.empty,declIdents, absolute) "?" false in
								let ident = StrMap.find "?" map in
								let refPath = (match expr with
										| APrimaryExpr _ -> APrimaryExpr (AIdentifier "window")
										| AFunctionExpr _ -> APrimaryExpr (AIdentifier "window")
										| AMemberBracketExpr (expr,_) -> expr
										| AMemberDotExpr (expr,_) -> expr
										| AMemberNewExpr _ -> APrimaryExpr (AIdentifier "window")) in
								let calls = StrMap.add ident (refPath,params,args,body) calls in
								calls, VUnknown, defaultRefValue, sideEffects1@sideEffects2@[(defaultRefValue,VUnknown)], (declIdents,labels,absolute) , variables, ANewExpression(
									AMemberExpression(APrimaryExpr(AIdentifier(ident)))
								)
							else
								
								calls, value, defaultRefValue, sideEffects1@sideEffects2@sideEffects, declIdents, variables, ACallExpression(ACallMemberArgs(expr,args))
						| _ -> let sideEffects = sideEffects1@sideEffects2@[(defaultRefValue,VUnknown)] in
							let variables = erase_sideEffects variables isCertain context sideEffects in
							calls, VUnknown, defaultRefValue, sideEffects, declIdents, variables, ACallExpression(ACallMemberArgs(expr,args))
					)
				| _ -> 
					let sideEffects = sideEffects1@sideEffects2@[(defaultRefValue,VUnknown)] in
					let variables = erase_sideEffects variables isCertain context sideEffects in
					calls, VUnknown, defaultRefValue, sideEffects, declIdents, variables, ACallExpression(ACallMemberArgs(expr,args))
			)
		| ACallExpressionArgs(expr, args) -> 
			let calls1, exprValue, refValue, sideEffects1, declIdents, variables, expr = 
				inline_callExpression can_inline declIdents variables isCertain context expr in
			let calls2, values, sideEffects2, declIdents, variables, args = 
				inline_arguments can_inline declIdents variables isCertain context args in
			let (*can_inline*) _ = can_inline && (sideEffects2 = []) in
			let calls = Tools_structs.merge_maps calls1 calls2 in
			(match exprValue with
				| VMemory index -> 
					(match memory_get index with
						| MFunction(_,params,body,_,_) -> let value, sideEffects = 
							inline_realFunctionContent variables isCertain context params body values in
							calls, value, defaultRefValue, sideEffects1@sideEffects2@sideEffects, declIdents, variables, (match expr with
								| ACallExpression expr -> ACallExpression(ACallExpressionArgs(expr,args))
								| ANewExpression expr -> 
									(match expr with
										| AMemberExpression expr -> ACallExpression(ACallMemberArgs(expr,args))
										| _ -> ANewExpression expr
									)
							)
						| _ -> let sideEffects = sideEffects1@sideEffects2@[(defaultRefValue,VUnknown)] in
							let variables = erase_sideEffects variables isCertain context sideEffects in 
							calls, VUnknown, defaultRefValue, sideEffects, declIdents, variables, (match expr with
								| ACallExpression expr -> ACallExpression(ACallExpressionArgs(expr,args))
								| ANewExpression expr -> 
									(match expr with
										| AMemberExpression expr -> ACallExpression(ACallMemberArgs(expr,args))
										| _ -> ANewExpression expr
									)
							)
					)
				| _ ->
					let sideEffects = sideEffects1@sideEffects2@[(defaultRefValue,VUnknown)] in
					let variables = erase_sideEffects variables isCertain context sideEffects in 
					calls, VUnknown, defaultRefValue, sideEffects, declIdents, variables, (match expr with
						| ACallExpression expr -> ACallExpression(ACallExpressionArgs(expr,args))
						| ANewExpression expr -> 
							(match expr with
								| AMemberExpression expr -> ACallExpression(ACallMemberArgs(expr,args))
								| _ -> ANewExpression expr
							)
					)
			)
			(* FIN *)
		| ACallExpressionExpr(expr1, expr2) -> 
			let calls1, exprValue1, refValue1, sideEffects1, declIdents, variables, expr1 = 
				inline_callExpression can_inline declIdents variables isCertain context expr1 in
			let calls2, exprValue2, _, sideEffects2, declIdents, variables, expr2 = 
				inline_expression can_inline declIdents variables isCertain context expr2 in
			let refValue = {
				ref_value = exprValue2;
				ref_index = (match exprValue1 with
					| VMemory index -> Some index;
					| _ -> None )
			} in
			let context = (match exprValue1 with
				| VMemory index -> {
					co_context = index;
					co_current = Some index;
					co_nesting = context.co_nesting
					}
				| _ -> context) in
			let exprValue, sideEffects3, variables = 
				(match exprValue1 with
					| VMemory index -> get_value variables isCertain context exprValue2
					| _ -> VUnknown, [], variables) in
			(Tools_structs.merge_maps calls1 calls2), exprValue, refValue, sideEffects1@sideEffects2@sideEffects3, declIdents, variables, (match expr1 with
					| ACallExpression expr1 -> ACallExpression(ACallExpressionExpr(expr1,expr2))
					| ANewExpression expr1 -> ANewExpression(expr1)
				)
		| ACallExpressionIdent(expr,ident) ->  
			let calls, exprValue, refValue, sideEffects, declIdents, variables, expr = 
				inline_callExpression can_inline declIdents variables isCertain context expr in
			let refValue = {
				ref_value = VString ident;
				ref_index = (match exprValue with
					| VMemory index -> Some index;
					| _ -> None )
			} in
			let context = (match exprValue with
				| VMemory index -> {
					co_context = index;
					co_current = Some index;
					co_nesting = context.co_nesting
					}
				| _ -> context) in
			let exprValue, sideEffects1, variables = 
				(match exprValue with
					| VMemory index -> get_value variables isCertain context (VString ident)
					| _ -> VUnknown, [], variables) in
			calls, exprValue, refValue, sideEffects@sideEffects1, declIdents, variables, (match expr with
					| ACallExpression expr -> ACallExpression(ACallExpressionIdent(expr,ident))
					| ANewExpression expr -> ANewExpression(expr)
				)

	and inline_conditionalExpression can_inline declIdents variables isCertain context = function
		| ACondBinaryExpr(expr) -> 
			let calls, exprValue, refValue, sideEffects, declIdents, variables, expr = 
				inline_binaryExpression can_inline declIdents variables isCertain context expr in
			calls, exprValue, refValue, sideEffects, declIdents, variables, ACondBinaryExpr(expr)
		| ACondTernaryExpr(expr1,expr2,expr3) ->
			let calls1, testValue, _, sideEffects1, declIdents, variables, expr1 = 
				inline_binaryExpression can_inline declIdents variables isCertain context expr1 in
			match testValue with
				| VBool false | VString "" | VNumber 0. ->
					let calls3, exprValue, refValue, sideEffects3, declIdents, variables, expr3 = 
						inline_assignmentExpression can_inline declIdents variables isCertain context expr3 in
						(Tools_structs.merge_maps calls1 calls3), exprValue, refValue, sideEffects1@sideEffects3, declIdents, variables, 
						ACondTernaryExpr(expr1,expr2,expr3)
					
				| VBool true | VString _ | VNumber _ | VMemory _ -> 
					let calls2, exprValue, refValue, sideEffects2, declIdents, variables, expr2 = 
						inline_assignmentExpression can_inline declIdents variables isCertain context expr2 in
						(Tools_structs.merge_maps calls1 calls2), exprValue, refValue, sideEffects1@sideEffects2, declIdents, variables, 
						ACondTernaryExpr(expr1,expr2,expr3)
				| _ -> 
				let calls2, exprValue2, _, sideEffects2, declIdents, variables, expr2 = 
					inline_assignmentExpression false declIdents variables false context expr2 in
				let calls3, exprValue3, _, sideEffects3, declIdents, variables, expr3 = 
					inline_assignmentExpression false declIdents variables false context expr3 in
				let variables = erase_sideEffects variables isCertain context (sideEffects2@sideEffects3) in
				(Tools_structs.merge_maps_list (calls1::calls2::calls3::[])), (merge_types exprValue2 exprValue3), defaultRefValue, sideEffects1@sideEffects2@sideEffects3, declIdents, variables, 
					ACondTernaryExpr(expr1,expr2,expr3)

	and inline_memberExpression can_inline declIdents variables isCertain isNode context = function
		| APrimaryExpr(expr) -> 
			let calls, exprValue, refValue, sideEffects, declIdents, variables, expr = 
				inline_primaryExpression can_inline declIdents variables isCertain context expr in
			calls, exprValue, refValue, (if isNode then context else match exprValue with
				| VMemory index -> {
					co_context = context.co_context;
					co_current = Some index;
					co_nesting = context.co_nesting
					}
				| _ -> context), sideEffects, declIdents, variables, APrimaryExpr(expr)
		| AFunctionExpr(expr) -> 
			let exprValue, refValue, sideEffects, declIdents, variables, expr = 
				inline_functionExpression declIdents variables isCertain context expr in
			StrMap.empty, exprValue, refValue, (if isNode then context else match exprValue with
				| VMemory index -> {
					co_context = context.co_context;
					co_current = Some index;
					co_nesting = context.co_nesting
					}
				| _ -> context), sideEffects, declIdents, variables, AFunctionExpr(expr)
		| AMemberBracketExpr(expr1,expr2) -> 
			let calls1, exprValue1, refValue1, context, sideEffects1, declIdents, variables, expr1 = 
				inline_memberExpression can_inline declIdents variables isCertain false context expr1 in
			let calls2, exprValue2, refValue2, sideEffects2, declIdents, variables, expr2 = 
				inline_expression can_inline declIdents variables isCertain context expr2 in
			let refValue = {
				ref_value = exprValue2;
				ref_index = (match exprValue1 with
					| VMemory index -> Some index
					| _ -> None)
			} in
			
			let exprValue, sideEffects3, variables = 
				(match exprValue1 with
					| VMemory index -> get_value variables isCertain context exprValue2
					| _ -> VUnknown, [], variables) in
					
			(Tools_structs.merge_maps calls1 calls2), exprValue, refValue, (if isNode then context else match exprValue with
				| VMemory index -> {
					co_context = context.co_context;
					co_current = Some index;
					co_nesting = context.co_nesting
					}
				| _ -> context), sideEffects1@sideEffects2@sideEffects3, declIdents, variables, AMemberBracketExpr(expr1,expr2)
		| AMemberDotExpr(expr,ident) -> 
			let calls, exprValue, refValue, context, sideEffects, declIdents, variables, expr = 
				inline_memberExpression can_inline declIdents variables isCertain false context expr in

			let refValue = {
				ref_value = VString ident;
				ref_index = (match exprValue with
					| VMemory index -> Some index
					| _ -> None)
			} in
			
			let exprValue, sideEffects1, variables = 
				(match exprValue with
					| VMemory index -> get_value variables isCertain context (VString ident)
					| _ -> VUnknown, [], variables) in

			calls, exprValue, refValue, (if isNode then context else match exprValue with
				| VMemory index -> {
					co_context = context.co_context;
					co_current = Some index;
					co_nesting = context.co_nesting
					}
				| _ -> context), sideEffects@sideEffects1, declIdents, variables, AMemberDotExpr(expr,ident)
		| AMemberNewExpr(expr,args) ->
			let calls1, exprValue, _, context, sideEffects1, declIdents, variables, expr = 
				inline_memberExpression can_inline declIdents variables isCertain false context expr in
			let calls2, values, sideEffects2, declIdents, variables, args = inline_arguments can_inline declIdents variables isCertain context args in
			let calls = (Tools_structs.merge_maps calls1 calls2) in
			let newIndex = memory_push (MObject((StrMap.add "constructor" exprValue StrMap.empty),StrMap.empty,StrMap.empty,true)) in
			(match exprValue with
				| VMemory index -> 
					(match memory_get index with
						| MFunction (_,params,body,_,_) -> 
							let context = {
								co_context = context.co_context;
								co_current = Some newIndex;
								co_nesting = context.co_nesting
							} in
							let _, sideEffects = inline_realFunctionContent variables isCertain context params body values in
							calls, VMemory(newIndex), defaultRefValue, context, sideEffects1@sideEffects2@sideEffects, declIdents, variables, AMemberNewExpr(expr,args)
						| _ -> calls, VMemory(newIndex), defaultRefValue, context, sideEffects1@sideEffects2@[(defaultRefValue,VUnknown)], declIdents, variables, AMemberNewExpr(expr,args)
					)
				| _ -> calls, VMemory(newIndex), defaultRefValue, context, sideEffects1@sideEffects2@[(defaultRefValue,VUnknown)], declIdents, variables, AMemberNewExpr(expr,args)
			)
			
	and inline_arguments can_inline declIdents variables isCertain context = function
		| [] -> StrMap.empty, [], [], declIdents, variables, []
		| expr::args -> let calls1, value, _, sideEffects1, declIdents, variables, expr = 
			inline_assignmentExpression can_inline declIdents variables isCertain context expr in
			let calls2, values, sideEffects2, declIdents, variables, args = 
			inline_arguments can_inline declIdents variables isCertain context args in
			(Tools_structs.merge_maps calls1 calls2), value::values, sideEffects1@sideEffects2, declIdents, variables, expr::args

	and  inline_binaryExpression can_inline declIdents variables isCertain context = function
		| AUnaryExpr(expr) -> let calls, exprValue, refValue, sideEffects, declIdents, variables, expr = 
				inline_unaryExpression can_inline declIdents variables isCertain context expr in
			calls, exprValue, refValue, sideEffects, declIdents, variables, AUnaryExpr(expr)
		| ABinaryExpr(op,expr1,expr2) -> let calls1, exprValue1, refValue, sideEffects1, declIdents, variables, expr1 = 
				inline_binaryExpression can_inline declIdents variables isCertain context expr1 in
			let calls, exprValue, refValue, sideEffects2, declIdents, variables, expr = (match op with
			| "||" -> (match exprValue1 with
				| VString "" | VBool false | VNumber 0. | VUnknownButFalse | VNull | VUndefined -> 
					let calls2, exprValue2, refValue, sideEffects2, declIdents, variables, expr2 = 
					inline_binaryExpression can_inline declIdents variables isCertain context expr2 in
					(Tools_structs.merge_maps calls1 calls2), exprValue2, refValue, sideEffects2, declIdents, variables, ABinaryExpr(op, expr1, expr2)
				| VString _ | VBool true | VNumber _ | VUnknownButTrue | VMemory _ -> 
					calls1, exprValue1, refValue, [], declIdents, variables, ABinaryExpr(op, expr1, expr2)
				| VUnknown -> let _, exprValue2, refValue, sideEffects2, declIdents, variables, expr2 = 
					inline_binaryExpression false declIdents variables isCertain context expr2 in
					calls1, VUnknown, refValue, sideEffects2, declIdents, variables, ABinaryExpr(op, expr1, expr2)
				)
			| "&&" -> (match exprValue1 with
				| VString "" | VBool false | VNumber 0. | VUnknownButFalse | VNull | VUndefined -> 
					calls1, exprValue1, refValue, [], declIdents, variables, ABinaryExpr(op, expr1, expr2)
				| VString _ | VBool true | VNumber _ | VUnknownButTrue | VMemory _ -> 
					let calls2, exprValue2, refValue, sideEffects2, declIdents, variables, expr2 = 
					inline_binaryExpression can_inline declIdents variables isCertain context expr2 in
					(Tools_structs.merge_maps calls1 calls2), exprValue2, refValue, sideEffects2, declIdents, variables, ABinaryExpr(op, expr1, expr2)
				| VUnknown -> let _, exprValue2, refValue, sideEffects2, declIdents, variables, expr2 = 
					inline_binaryExpression false declIdents variables isCertain context expr2 in
					calls1, VUnknown, refValue, sideEffects2, declIdents, variables, ABinaryExpr(op, expr1, expr2)
				)
			| "==" -> let calls2, exprValue2, refValue, sideEffects2, declIdents, variables, expr2 = 
				inline_binaryExpression can_inline declIdents variables isCertain context expr2 in
				let exprValue = (match exprValue1 with
					| VString s1 -> (match exprValue2 with
							| VString s2 -> VBool (s1 = s2)
							| VBool _ | VNumber _ | VUndefined | VNull -> VBool false
							| _ -> VUnknown
						)
					| VBool b1 -> (match exprValue2 with
						| VBool b2 -> VBool (b1 = b2)
						| VNumber 0. -> VBool (b1 = false)
						| VNumber 1. -> VBool b1
						| VString _ | VNumber _ | VNull -> VBool false
						| _ -> VUnknown )
					| VMemory index1 -> (match exprValue2 with
						| VMemory index2 -> VBool (index1 = index2)
						| VBool _ | VNumber _ | VNull | VUndefined | VString _ -> VBool false
						| _ -> VUnknown )
					| VNumber i1 -> (match exprValue2 with
						| VNumber i2 -> VBool (i1 = i2)
						| VBool true -> VBool (i1 = 1.)
						| VBool false -> VBool (i1 = 0.)
						| VString _ | VNull | VUndefined -> VBool false
						| VUnknownButFalse -> if i1 = 1. then VBool false else VUnknown
						| VUnknownButTrue -> if i1 = 0. then VBool false else VUnknown
						| _ -> VUnknown )
					| _ -> VUnknown
				) in
				(Tools_structs.merge_maps calls1 calls2), exprValue, refValue, sideEffects2, declIdents, variables, ABinaryExpr(op, expr1, expr2)
			| "!=" -> let calls2, exprValue2, refValue, sideEffects2, declIdents, variables, expr2 = 
				inline_binaryExpression can_inline declIdents variables isCertain context expr2 in
				let exprValue = (match exprValue1 with
					| VString s1 -> (match exprValue2 with
							| VString s2 -> VBool (s1 <> s2)
							| VBool _ | VNumber _ | VUndefined | VNull -> VBool true
							| _ -> VUnknown
						)
					| VBool b1 -> (match exprValue2 with
						| VBool b2 -> VBool (b1 <> b2)
						| VNumber 0. -> VBool b1
						| VNumber 1. -> VBool (b1 = false)
						| VString _ | VNumber _ | VNull -> VBool true
						| _ -> VUnknown )
					| VMemory index1 -> (match exprValue2 with
						| VMemory index2 -> VBool (index1 <> index2)
						| VBool _ | VNumber _ | VNull | VUndefined | VUnknownButFalse | VString _ -> VBool true
						| _ -> VUnknown )
					| VNumber i1 -> (match exprValue2 with
						| VNumber i2 -> VBool (i1 <> i2)
						| VBool true -> VBool (i1 <> 1.)
						| VBool false -> VBool (i1 <> 0.)
						| VString _ | VNull | VUndefined -> VBool true
						| VUnknownButFalse -> if i1 = 1. then VBool true else VUnknown
						| VUnknownButTrue -> if i1 = 0. then VBool true else VUnknown
						| _ -> VUnknown )
					| _ -> VUnknown
				) in
				(Tools_structs.merge_maps calls1 calls2), exprValue, refValue, sideEffects2, declIdents, variables, ABinaryExpr(op, expr1, expr2)
			| "+" -> let calls2, exprValue2, refValue, sideEffects2, declIdents, variables, expr2 = 
				inline_binaryExpression can_inline declIdents variables isCertain context expr2 in
				let exprValue = (match exprValue1 with
					| VNumber i1 -> (match exprValue2 with
						| VNumber i2 -> VNumber (i1 +. i2)
						| VString i2 -> VString ((string_of_flint i1) ^ i2)
						| VBool i2 -> VNumber (i1 +. (if i2 then 1. else 0.))
						| _ -> VUnknown )
					| VString i1 -> (match exprValue2 with
						| VString i2 -> VString (i1 ^ i2)
						| VNumber i2 -> VString (i1 ^ (string_of_flint i2))
						| VBool i2 -> VString (i1 ^ (if i2 then "true" else "false"))
						| _ -> VUnknown)
					| VBool i1 -> (match exprValue2 with
						| VString i2 -> VString ((if i1 then "true" else "false") ^ i2)
						| VNumber i2 -> VNumber ((if i1 then 1. else 0.) +. i2)
						| VBool i2 -> VNumber ((if i1 then 1. else 0.) +. (if i2 then 1. else 0.))
						| _ -> VUnknown
						)
					| _ -> VUnknown
				) in
				(Tools_structs.merge_maps calls1 calls2), exprValue, refValue, sideEffects2, declIdents, variables, ABinaryExpr(op, expr1, expr2)
			| "-" -> let calls2, exprValue2, refValue, sideEffects2, declIdents, variables, expr2 = 
				inline_binaryExpression can_inline declIdents variables isCertain context expr2 in
				let exprValue = (match exprValue1 with
					| VNumber i1 -> (match exprValue2 with
						| VNumber i2 -> VNumber (i1 -. i2)
						| _ -> VUnknown )
					| _ -> VUnknown
				) in
				(Tools_structs.merge_maps calls1 calls2), exprValue, refValue, sideEffects2, declIdents, variables, ABinaryExpr(op, expr1, expr2)
			| "*" -> let calls2, exprValue2, refValue, sideEffects2, declIdents, variables, expr2 = 
				inline_binaryExpression can_inline declIdents variables isCertain context expr2 in
				let exprValue = (match exprValue1 with
					| VNumber i1 -> (match exprValue2 with
						| VNumber i2 -> VNumber (i1 *. i2)
						| _ -> VUnknown )
					| _ -> VUnknown
				) in
				(Tools_structs.merge_maps calls1 calls2), exprValue, refValue, sideEffects2, declIdents, variables, ABinaryExpr(op, expr1, expr2)
			| "/" -> let calls2, exprValue2, refValue, sideEffects2, declIdents, variables, expr2 = 
				inline_binaryExpression can_inline declIdents variables isCertain context expr2 in
				let exprValue = (match exprValue1 with
					| VNumber i1 -> (match exprValue2 with
						| VNumber i2 -> VNumber (i1 /. i2)
						| _ -> VUnknown )
					| _ -> VUnknown
				) in
				(Tools_structs.merge_maps calls1 calls2), exprValue, refValue, sideEffects2, declIdents, variables, ABinaryExpr(op, expr1, expr2)
			| "<" -> let calls2, exprValue2, refValue, sideEffects2, declIdents, variables, expr2 = 
				inline_binaryExpression can_inline declIdents variables isCertain context expr2 in
				let exprValue = (match exprValue1 with
					| VNumber i1 -> (match exprValue2 with
						| VNumber i2 -> VBool (i1 < i2)
						| _ -> VUnknown )
					| _ -> VUnknown
				) in
				(Tools_structs.merge_maps calls1 calls2), exprValue, refValue, sideEffects2, declIdents, variables, ABinaryExpr(op, expr1, expr2)
			| ">" -> let calls2, exprValue2, refValue, sideEffects2, declIdents, variables, expr2 = 
				inline_binaryExpression can_inline declIdents variables isCertain context expr2 in
				let exprValue = (match exprValue1 with
					| VNumber i1 -> (match exprValue2 with
						| VNumber i2 -> VBool (i1 > i2)
						| _ -> VUnknown )
					| _ -> VUnknown
				) in
				(Tools_structs.merge_maps calls1 calls2), exprValue, refValue, sideEffects2, declIdents, variables, ABinaryExpr(op, expr1, expr2)
			| _ -> let calls2, exprValue2, refValue, sideEffects2, declIdents, variables, expr2 = 
				inline_binaryExpression can_inline declIdents variables isCertain context expr2 in
					(Tools_structs.merge_maps calls1 calls2), VUnknown, refValue, sideEffects2, declIdents, variables, ABinaryExpr(op, expr1, expr2)
			) in
			calls, exprValue, refValue, sideEffects1@sideEffects2, declIdents, variables, expr

	and inline_primaryExpression can_inline declIdents variables isCertain context = function
		| AThis -> StrMap.empty, VMemory(context.co_context), {
				ref_value = VString "this";
				ref_index = None
			}, [], declIdents, variables, AThis
		| AIdentifier(ident) -> 
			let exprValue, _, variables = get_value variables isCertain context (VString ident) in
			StrMap.empty, exprValue, {
				ref_value = VString(ident);
				ref_index = None
			}, [], declIdents, variables, AIdentifier(ident)
		| ALiteral(literal) -> let exprValue, refValue, sideEffects, declIdents, variables, literal = 
				inline_literal declIdents variables isCertain context literal in
			StrMap.empty, exprValue, refValue, sideEffects, declIdents, variables, ALiteral(literal)
		| AArrayLiteral(literal) -> let calls, exprValue, refValue, sideEffects, declIdents, variables, literal = 
				inline_arrayLiteral can_inline declIdents variables isCertain context literal in
			calls, exprValue, refValue, sideEffects, declIdents, variables, AArrayLiteral(literal)
		| AObjectLiteral(literal) -> let calls, exprValue, refValue, sideEffects, declIdents, variables, literal =
				inline_objectLiteral can_inline declIdents variables isCertain context literal in
			calls, exprValue, refValue, sideEffects, declIdents, variables, AObjectLiteral(literal)
		| AExpression(expr) -> let calls, exprValue, refValue, sideEffects, declIdents, variables, expr = 
				inline_expression can_inline declIdents variables isCertain context expr in
			calls, exprValue, refValue, sideEffects, declIdents, variables, AExpression(expr)

	and inline_unaryExpression can_inline declIdents variables isCertain context = function
		| APostfixExpr(expr) ->
			let calls, exprValue, refValue, sideEffects, declIdents, variables, expr = 
			inline_postfixExpression can_inline declIdents variables isCertain context expr in
			calls, exprValue, refValue, sideEffects, declIdents, variables, APostfixExpr(expr)
		| AOpUnaryExpr(op, expr) ->
			let calls, exprValue, refValue, sideEffects, declIdents, variables, expr = 
			inline_unaryExpression can_inline declIdents variables isCertain context expr in
			let exprValue, sideEffects2, variables = match op with
				| "--" -> (match exprValue with
					| VNumber n -> let sideEffects, variables = set_value variables isCertain context (VNumber (n-.1.)) refValue.ref_value in 
						(VNumber (n-.1.)), sideEffects@[(refValue, VNumber (n-.1.))], variables
					| _ -> let sideEffects, variables = set_value variables isCertain context VUnknown refValue.ref_value in
						VUnknown, sideEffects@[(refValue, VUnknown)], variables 
					)
				| "++" -> (match exprValue with
					| VNumber n -> let sideEffects, variables = set_value variables isCertain context (VNumber (n+.1.)) refValue.ref_value in 
						(VNumber (n+.1.)), sideEffects@[(refValue, VNumber (n+.1.))], variables
					| _ -> let sideEffects, variables = set_value variables isCertain context VUnknown refValue.ref_value in
						VUnknown, sideEffects@[(refValue, VUnknown)], variables )
				| "!" -> (match exprValue with
					| VBool b -> VBool (not b)
					| VUnknownButTrue -> VBool false
					| VUnknownButFalse -> VBool true
					| VNumber 0. -> VBool true
					| VNumber _ -> VBool false
					| _ -> VUnknown ), [], variables
				| "+" -> (match exprValue with
					| VNumber n -> VNumber n
					| VBool true -> VNumber 1.
					| VBool false -> VNumber 0.
					| _ -> VUnknown ), [], variables
				| "-" -> (match exprValue with
					| VNumber n -> VNumber (-. n)
					| VBool true -> VNumber (-1.)
					| VBool false -> VNumber 0.
					| VString _ -> VUnknownButFalse
					| _ -> VUnknown ), [], variables
				| "~" -> (match exprValue with
					| VNumber 0. -> VNumber (-1.)
					| VNumber _ -> VUnknownButTrue 
					| VBool true -> VNumber (-2.)
					| VBool false -> VNumber (-1.)
					| VString _ -> VNumber (-1.)
					| VMemory _ -> VNumber (-1.)
					| _ -> VUnknown ), [], variables
				| "void" -> VUndefined, [], variables
				| "delete" -> (match exprValue with
					| VNumber _ -> (VBool true), [], variables
					| VString _ -> (VBool true), [], variables
					| VBool _ -> (VBool true), [], variables
					| VMemory _ -> let sideEffects, variables = set_value variables isCertain context VUnknown refValue.ref_value in 
							VUnknown, sideEffects@[refValue,VUnknown], variables
					| _ -> VUnknown, sideEffects@[refValue,VUnknown], variables)
				| "typeof" -> (match exprValue with
					| VNumber _ -> VString "number"
					| VString _ -> VString "string"
					| VMemory index -> ( (match memory_get index with
						| MFunction _ -> VString "function"
						| MUnknown -> VUnknownButTrue
						| _ -> VString "object" ))
					| _ -> VUnknownButTrue), [], variables
				| _ -> VUnknown, [], variables in
				calls, exprValue, refValue, sideEffects@sideEffects2, declIdents, variables, AOpUnaryExpr(op, expr)
	
	and inline_literal declIdents variables isCertain context = function
		| ANull -> VNull, defaultRefValue, [], declIdents, variables, ANull
		| ABooleanLiteral(value) -> VBool(value), defaultRefValue, [], declIdents, variables, ABooleanLiteral(value)
		| ALitNumericLiteral(literal) -> VNumber(float_of_string literal), defaultRefValue, [], declIdents, variables, ALitNumericLiteral(literal)
		| ALitStringLiteral(literal) -> VString(literal), defaultRefValue, [], declIdents, variables, ALitStringLiteral(literal)
		| ARegularExpressionLiteral(regularExpression) -> let index = memory_push (MObject(StrMap.empty,StrMap.empty,StrMap.empty,false)) in
			VMemory(index), defaultRefValue, [], declIdents, variables, ARegularExpressionLiteral(regularExpression)
		
	and inline_arrayLiteral can_inline declIdents variables isCertain context = function
		| AElisions(elisions) -> 
			let lst = List.fold_left (fun lst _ -> lst@[VUndefined]) [] elisions in
			let index = create_array lst in
			StrMap.empty, VMemory(index), defaultRefValue, [], declIdents, variables, AElisions(elisions)
		| AElements(elements) ->  
			let calls, lst, sideEffects, declIdents, variables, elements = 
			List.fold_left (fun (calls2, lst, sideEffects, declIdents, variables, elements) e -> 
				let calls1, lst1, sideEffects1, declIdents, variables, element = 
				inline_element can_inline declIdents variables isCertain context e in
				(Tools_structs.merge_maps calls1 calls2), lst@lst1, sideEffects@sideEffects1, declIdents, variables, elements@[element]) 
				(StrMap.empty, [], [], declIdents, variables, []) elements in
			let index = create_array lst in
			calls, VMemory(index), defaultRefValue, sideEffects, declIdents, variables, AElements(elements)
		| AElementsAndElisions(elements, elisions) ->
			let calls, lst, sideEffects, declIdents, variables, elements = 
			List.fold_left (fun (calls2, lst, sideEffects, declIdents, variables, elements) element -> 
				let calls1, lst1, sideEffects1, declIdents, variables, element = 
				inline_element can_inline declIdents variables isCertain context element in
				(Tools_structs.merge_maps calls1 calls2), lst@lst1, sideEffects@sideEffects1, declIdents, variables, elements@[element]) 
				(StrMap.empty, [], [], declIdents, variables, []) elements in
			let lst = List.fold_left (fun lst  _ -> lst@[VUndefined]) lst elisions in
			let index = create_array lst in
			calls, VMemory(index), defaultRefValue, sideEffects, declIdents, variables, AElementsAndElisions(elements,elisions)

	and inline_element can_inline declIdents variables isCertain context (elisions, expr) =
		let lst = List.fold_left (fun lst  _ -> lst@[VUndefined]) [] elisions in
		let calls, exprValue, refValue, sideEffects, declIdents, variables, expr = 
		inline_assignmentExpression can_inline declIdents variables isCertain context expr in
		calls, lst@[exprValue], sideEffects, declIdents, variables, (elisions,expr)
		
	and inline_objectLiteral can_inline declIdents variables isCertain context exprs =
		let calls, both,getter,setter, sideEffects, declIdents, variables, exprs = 
			List.fold_left (fun (calls2, both, getter, setter, sideEffects, declIdents, variables, exprs) expr ->
			let calls1, both, getter, setter, sideEffects1, declIdents, variables, expr = 
				inline_propertyAssignment can_inline both getter setter declIdents variables isCertain context expr in
			(Tools_structs.merge_maps calls1 calls2), both, getter, setter, sideEffects@sideEffects1, declIdents, variables, exprs@[expr]) 
			(StrMap.empty,StrMap.empty,StrMap.empty,StrMap.empty, [],declIdents,variables,[]) exprs in
		let index = create_object both getter setter in
		calls, VMemory(index), defaultRefValue, sideEffects, declIdents, variables, exprs

	and inline_propertyAssignment can_inline both getter setter declIdents variables isCertain context = function
		| APropAssignment(name,expr) -> let key = inline_propertyName name in
			let calls, exprValue, _, sideEffects, declIdents, variables, expr = 
			inline_assignmentExpression can_inline declIdents variables isCertain context expr in
			calls, (StrMap.add key exprValue both), getter, setter, sideEffects, declIdents, variables, APropAssignment(name,expr)
		| APropGet(name,body) -> let key = inline_propertyName name in
			let _,sideEffects,newBody = inline_functionContent declIdents variables isCertain context [] body in
			(*let index = create_function is_inline [] body true in*)
			StrMap.empty,both, (StrMap.add key (false, [],body) getter), setter, sideEffects, declIdents, variables, APropGet(name,newBody)
		| APropSet(name,params,body) -> let key = inline_propertyName name in
			let _,sideEffects,newBody = inline_functionContent declIdents variables isCertain context params body in
			(*let index = create_function is_inline params body true in*)
			StrMap.empty,both, getter, (StrMap.add key (false,params,body) setter), sideEffects, declIdents, variables, APropSet(name,params,newBody)
	
	and inline_propertyName = function
		| AIdentifierName(str) -> str
		| AStringLiteral(str) -> str
		| ANumericLiteral(str) -> string_of_flint (float_of_string str)
	
	and inline_postfixExpression can_inline declIdents variables isCertain context = function
		| ALeftHandSide(expr) -> let calls, exprValue, refValue, sideEffects, declIdents, variables, expr = 
				inline_leftHandSideExpression can_inline declIdents variables isCertain context expr in
			calls, exprValue, refValue, sideEffects, declIdents, variables, ALeftHandSide(expr)
		| ALeftHandSideOp(expr,op) -> let calls, exprValue, refValue, sideEffects, declIdents, variables, expr = 
				inline_leftHandSideExpression can_inline declIdents variables isCertain context expr in
			let sideEffects2, variables = match op with
			| "--" -> (match exprValue with
					| VNumber n -> let sideEffects, variables = set_value variables isCertain context (VNumber (n-.1.)) refValue.ref_value in 
						sideEffects@[(refValue, VNumber (n-.1.))], variables
					| _ -> let sideEffects, variables = set_value variables isCertain context VUnknown refValue.ref_value in
						sideEffects@[(refValue, VUnknown)], variables 
					)
			| "++" -> (match exprValue with
					| VNumber n -> let sideEffects, variables = set_value variables isCertain context (VNumber (n+.1.)) refValue.ref_value in 
						sideEffects@[(refValue, VNumber (n+.1.))], variables
					| _ -> let sideEffects, variables = set_value variables isCertain context VUnknown refValue.ref_value in
						sideEffects@[(refValue, VUnknown)], variables 
					)
			| _ -> [], variables in
		calls, exprValue, refValue, sideEffects@sideEffects2, declIdents, variables, ALeftHandSideOp(expr,op)

	in

	let ast2 = inline_program ast in
	if ast2 = ast then ast2 else inline ast2
